﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Validity.Contexts;
using Validity.Rules;
using Validity.Rules.Decorators;

namespace Validity.Test.Contexts
{
  [TestClass]
  public class ValidationContextTest
  {
    private readonly MockValidationContext<IComparable> _context = new MockValidationContext<IComparable>();

    [TestMethod]
    public void TestDefaultCtor()
    {
      Assert.IsNotNull(new ValidationContext<ObjectStub, IComparable>());
    }

    [TestMethod]
    public void TestAddRule()
    {
      var context = new ValidationContext<ObjectStub, IComparable>(_context);
      var rule = new Rule<ObjectStub, IComparable>((obj, val) => true, (obj, val) => "Message");

      _context.RulesMock.Setup(mock => mock.Add(rule));

      Assert.IsNotNull(context.AddRule(rule));
    }

    [TestMethod]
    public void TestDecorateLastRuleWithWhenListEmpty()
    {
      var context = new ValidationContext<ObjectStub, IComparable>(_context);

      _context.RulesMock.Setup(mock => mock.Count).Returns(0);

      context.DecorateLastRuleWith(ruleToDecorate => ruleToDecorate);
    }

    [TestMethod]
    public void TestDecorateLastRuleWithWhenListNotEmpty()
    {
      var context = new ValidationContext<ObjectStub, IComparable>(_context);
      var rule = new Rule<ObjectStub, IComparable>((obj, val) => true, (obj, val) => "Message");
      var decoratedRule = new RuleDecorator<ObjectStub, IComparable>(rule);

      _context.RulesMock.Setup(mock => mock.Count).Returns(1);
      _context.RulesMock.SetupGet(mock => mock[0]).Returns(rule);
      _context.RulesMock.SetupSet(mock => mock[0] = decoratedRule);

      context.DecorateLastRuleWith(ruleToDecorate =>
      {
        Assert.AreSame(rule, ruleToDecorate);
        return decoratedRule;
      });
    }

    [TestMethod]
    public void TestGetRuleViolationsForReturnsViolationsWhenAvailable()
    {
      var context = new ValidationContext<ObjectStub, IComparable>(_context);
      var rule = new Mock<IRule<ObjectStub, IComparable>>(MockBehavior.Strict);
      var val = (IComparable)String.Empty;
      var obj = new ObjectStub();

      _context.RulesMock.Setup(mock => mock.GetEnumerator()).Returns(new List<IRule<ObjectStub, IComparable>> { rule.Object }.GetEnumerator());
      rule.Setup(mock => mock.IsViolatedBy(obj, val)).Returns(true);
      rule.Setup(mock => mock.GetRuleViolation(obj, val)).Returns(new RuleViolation("Violation"));

      Assert.AreEqual(1, context.GetRuleViolationsFor(obj, val).ToList().Count);
    }

    [TestMethod]
    public void TestGetRuleViolationsForReturnsEmptyWhenViolationsWhenNotAvailable()
    {
      var context = new ValidationContext<ObjectStub, IComparable>(_context);
      var rule = new Mock<IRule<ObjectStub, IComparable>>(MockBehavior.Strict);
      var val = (IComparable)String.Empty;
      var obj = new ObjectStub();

      _context.RulesMock.Setup(mock => mock.GetEnumerator()).Returns(new List<IRule<ObjectStub, IComparable>> { rule.Object }.GetEnumerator());
      rule.Setup(mock => mock.IsViolatedBy(obj, val)).Returns(false);

      Assert.AreEqual(0, context.GetRuleViolationsFor(obj, val).ToList().Count);
    }
  }
}
